package modviewcontrol;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.swt.custom.StyleRange;

public class NodeUtils {
	/*
	 * 	setRanges: sets style ranges for the passed root and its children
	 */
	public static boolean setRanges(Node root, String input, int flags){
		assert(root!=null&&input!=null);
		assert(root.getMatchedRanges()!=null);
		assert(root.getActualChildren()!=null);
//		System.out.println("setRanges called");
		try{
			resetRanges(root);
			setRanges(root,input,0,flags);
		}catch(Exception e){
			resetRanges(root);
			return false;
		}
		return true;
	}

	private static boolean setRanges(Node node, String input, int startIndex, int flags){
		assert(node!=null);
		assert(node.getMatchedRanges()!=null);
		assert(node.getActualChildren()!=null);

		if(input==null)
			return false;
		if(node.nodeType==Node.Type.PATTERN && node.getRegex() != null && !node.getRegex().equals("")){
//			System.out.println("SetRanges regex: " + node.getRegex());
//			System.out.println("SetRanges input: " + input);

			Matcher matcher = getMatcher(node.getRegex(),input,flags);
			int totalGroups = matcher.groupCount();

			while(matcher.find()){//make some ranges for each match

				StyleRange tmpRange = new StyleRange();
				tmpRange.start = startIndex + matcher.start();
				tmpRange.length = matcher.end() - matcher.start();
				tmpRange.background = node.getNodeColor();
				node.addRange(tmpRange);

//				System.out.println(tmpRange);

				int i = totalGroups;
				do{//make a range for each group
					for(Node child : node.getActualChildren()){
						setRanges(child, matcher.group(i), startIndex+matcher.start(i),flags);
					}
					i--;
				}while(i > 0);
			}
		}else{
			for(Node child : node.getActualChildren()){
				setRanges(child,input,startIndex,flags);
			}
		}

		return true;
	}

	private static Matcher getMatcher(String regex, String input, int flags){
		assert(regex!=null&&input!=null);
		return Pattern.compile(regex,flags).matcher(input);
	}

	private static void resetRanges(Node node){
		assert(node!=null);
		assert(node.getMatchedRanges()!=null);
		assert(node.getActualChildren()!=null);
		node.getMatchedRanges().clear();
		for(Node child : node.getActualChildren())
			resetRanges(child);
	}

	/*
	 * 		getXML: returns the XML extracted from the given input
	 */
	public static String getXML(Node root, String input, int flags){
		assert(root!=null&&input!=null);
		assert(root.getActualChildren()!=null);
		assert(root.getPossChildren()!=null);
		StringBuilder result = new StringBuilder();
		Map<Integer,List<StringBuilder>> tmp;

		try{
			tmp = getXML(root, input, 0, flags);
		}catch(Exception e){
			return null;
		}

		if(tmp!=null)
			for(List<StringBuilder> stringList: tmp.values())
				if(stringList!=null)
					for(StringBuilder word : stringList)
						if(word!=null)
							result.append(word);

		return result.toString();
	}

	private static Map<Integer, List<StringBuilder>> getXML(Node node, 
			String input, int startIndex,int flags){
		assert(node!=null&&input!=null);
		if(node.getActualChildren().isEmpty()&&node.getNodeType()!=Node.Type.ITEM){
			Node tmpItem = new Node();
			tmpItem.setNodeType(Node.Type.ITEM);
			node.getActualChildren().add(tmpItem);
			Map<Integer, List<StringBuilder>> result 
			= getXML(node, input,startIndex, flags);
			node.removeActualChild(tmpItem);
			return result;
		}
		if(node.nodeType==Node.Type.IMPORT)
			return importXML(node, input, startIndex, flags);
		if(node.nodeType==Node.Type.MULTI)
			return multiXML(node,input,startIndex,flags);
		if(node.nodeType==Node.Type.PATTERN)
			return patternXML(node,input,startIndex,flags);
		if(node.nodeType==Node.Type.SCHEME)
			return schemeXML(node,input,startIndex,flags);
		if(node.nodeType==Node.Type.SINGLE)
			return singleXML(node,input,startIndex,flags);
		if(node.nodeType==Node.Type.ITEM)
			return itemXML(node,input,startIndex,flags);

		return null;
	}

	private static Map<Integer, List<StringBuilder>> singleXML(Node node,
			String input, int startIndex, int flags) {
		assert(node!=null&&input!=null);
		Map<Integer, List<StringBuilder>> singleMap = schemeXML(node,input,startIndex,flags);

		if(singleMap==null)
			return null;

		List<StringBuilder> tmp = cross(singleMap);
		for(StringBuilder item : tmp){
			item.insert(0, ">\n");
			item.insert(0,node.getTitle());
			item.insert(0, "\n<");
			item.append("\n</");
			item.append(node.getTitle());
			item.append(">");
		}

		singleMap.clear();
		singleMap.put(node.getParentIdx(), tmp);
		return singleMap;
	}

	private static Map<Integer, List<StringBuilder>> join(
			Map<Integer, List<StringBuilder>> singleMap,
			Map<Integer, List<StringBuilder>> xml) {

		if(xml==null||singleMap==null)
			return (xml==null?singleMap : xml);

		for(int i : xml.keySet()){
			if(singleMap.containsKey(i)){
				List<StringBuilder> tmp = singleMap.get(i);
				tmp.addAll(xml.get(i));
				singleMap.put(i, tmp);
			}else{
				singleMap.put(i, xml.get(i));
			}
		}
		return singleMap;
	}

	private static Map<Integer, List<StringBuilder>> schemeXML(Node node,
			String input, int startIndex, int flags) {

		assert(node!=null && input!=null);
		Map<Integer, List<StringBuilder>> schemeMap = null;
		for(Node child : node.getActualChildren())
			schemeMap = join(schemeMap,getXML(child, input, startIndex, flags));

		return schemeMap;
	}

	private static Map<Integer, List<StringBuilder>> patternXML(Node node,
			String input, int startIndex, int flags) {
		assert(node!=null&&input!=null);
		Map<Integer, List<StringBuilder>> patternMap = null;
		Matcher matcher = getMatcher(node.getRegex(),input,flags);

		while(matcher.find()){
			int i = matcher.groupCount();
			do{
				String group = matcher.group(i);
				for(Node child : node.getActualChildren()){
					patternMap = join(patternMap, schemeXML(child, group, startIndex,flags));
				}
				i--;
			}while(i > 0);
		}
		if(patternMap != null){
			System.out.print("");
		}
		return patternMap;
	}

	private static Map<Integer, List<StringBuilder>> multiXML(Node node,
			String input, int startIndex, int flags) {
		assert(node!=null&&input!=null);
		return dot(singleXML(node,input,startIndex,flags));
	}

	private static Map<Integer, List<StringBuilder>> itemXML(Node node,
			String input, int startIndex, int flags) {

		assert(node!=null);
		if(input==null||input.length()==0)
			return null;

		Map<Integer, List<StringBuilder>> itemMap= new TreeMap<Integer, List<StringBuilder>>();
		List<StringBuilder> itemList = new LinkedList<StringBuilder>();
		itemList.add(new StringBuilder(input));
		itemMap.put(node.getParentIdx(), itemList);
		return itemMap;
	}

	private static Map<Integer, List<StringBuilder>> importXML(Node node,
			String input, int startIndex, int flags) {
		assert(node!=null&&input!=null);
		return null;
	}

	private static Map<Integer, List<StringBuilder>> dot(
			Map<Integer, List<StringBuilder>> singleXML) {

		if(singleXML==null)
			return null;

		assert(singleXML.size()==1);

		List<StringBuilder> result= new LinkedList<StringBuilder>();

		for(StringBuilder tmpStrB : singleXML.get(singleXML.keySet().toArray()[0])){
			if(result.isEmpty())
				result.add(tmpStrB);
			else
				((LinkedList<StringBuilder>)result).getFirst().append(tmpStrB);
		}

		singleXML.put((Integer)singleXML.keySet().toArray()[0], result);
		return singleXML;
	}

	private static List<StringBuilder> cross(
			Map<Integer, List<StringBuilder>> singleMap) {
		assert(singleMap!=null);
		List<StringBuilder> result = null;

		for(List<StringBuilder> tmp : singleMap.values())
			result = cross(result,tmp);

		return result;
	}

	private static List<StringBuilder> cross(List<StringBuilder> result,
			List<StringBuilder> tmp) {

		if(result==null)
			return tmp;

		for(StringBuilder a : result)
			for(StringBuilder b : tmp)
				a.append(b);

		return result;
	}

	public static String getBasicXML(Node node, ArrayList<String> inputArray, int flags, int tabs) {
		String result = "";

		if(node.toString().equalsIgnoreCase("child")){
			System.out.print("");
		}

		switch (node.nodeType){
		case PATTERN:{
			if(node.getActualChildren().isEmpty()){
				Pattern pattern = Pattern.compile(node.getRegex(),flags);

				for(String input:inputArray){
					if(input!=null){
						Matcher matcher = pattern.matcher(input);
						int totalGroups = matcher.groupCount();

						while(matcher.find()){
							int i = totalGroups;
							do{
								String group = matcher.group(i);
								result += NodeIO.makeTabs(tabs);
								result += group + '\n';
								i--;
							}while(i > 0);
						}
					}
				}
			}
			else{
				Pattern pattern = Pattern.compile(node.getRegex(),flags);

				for(String input:inputArray){
					if(input != null){
						Matcher matcher = pattern.matcher(input);
						int totalGroups = matcher.groupCount();
						ArrayList<String> matches = new ArrayList<String>();


						while(matcher.find()){
							int i = totalGroups;
							do{
								String group = matcher.group(i);
								matches.add(group);
								i--;
							}while(i > 0);
						}
						for(Node child:node.getActualChildren()){
							String fromChildren = getBasicXML(child,matches,flags,tabs+1);
							result += fromChildren;
						}
					}
				}
			}
			break;
		}
		case MULTI:{
			for(String input:inputArray){
				
				String toAdd = "";
				
				toAdd += NodeIO.makeTabs(tabs);
				toAdd += "<" + node.getTitle() + ">\n";

				ArrayList<String> single = new ArrayList<String>();
				single.add(input);
				
				for(Node child:node.getActualChildren()){
					
					String fromChild = getBasicXML(child,single,flags,tabs+1);

					if(fromChild.trim().equals("")){
						continue;
					}
					toAdd += fromChild;
				}
				toAdd += NodeIO.makeTabs(tabs);
				toAdd += "</" + node.getTitle() + ">\n";
				
				result += toAdd;
			}
			break;

		}
		case SCHEME:
		case SINGLE:{
			result += NodeIO.makeTabs(tabs);
			result += "<" + node.getTitle() + ">\n";

			String fromChildren = "";
			for(Node child:node.getActualChildren()){
				fromChildren += getBasicXML(child,inputArray,flags,tabs+1);
			}
			if(fromChildren.trim().equals("")){
				return "";
			}
			result += fromChildren;

			result += NodeIO.makeTabs(tabs);
			result += "</" + node.getTitle() + ">\n";
			break;
		}
		}
		return result;
	}
}
